#pragma once

#include "bsoncxx/builder/stream/document.hpp"
#include "bsoncxx/builder/stream/helpers.hpp"
#include "bsoncxx/document/value.hpp"
#include "bsoncxx/document/element.hpp"
#include "bsoncxx/types/value.hpp"
using bsoncxx::v_noabi::document::view;
using bsoncxx::builder::stream::document;
using bsoncxx::builder::stream::open_document;
using bsoncxx::builder::stream::open_array;
using bsoncxx::builder::stream::close_document;
using bsoncxx::builder::stream::close_array;

namespace pde
{
    #define to_bson_value(key_, context_) doc << key_ << context_;
    #define to_bson_value_datetime(key_, context_) doc << key_ << bsoncxx::types::b_date(std::chrono::milliseconds(context_));

    #define to_bson_vector(key_, context_) \
        doc << key_ << open_array << \
            [&](bsoncxx::builder::stream::array_context<> arr) {\
                for (auto d : context_) arr << d;\
            }\
            << close_array;

    #define to_bson_array(key_, context_, count) \
		doc << key_ << open_array <<\
            [&](bsoncxx::builder::stream::array_context<> arr)\
            {\
                for(int i=0; i < count; ++i)\
                    arr << context_[i];\
            }\
            << close_array;

    #define to_bson_obj(key_, context_) \
		if (context_)\
        {\
            std::string bstr = context_->to_bson();\
            auto tview = view(reinterpret_cast<const uint8_t*>(bstr.c_str()), bstr.length());\
            doc << key_ << bsoncxx::builder::concatenate(tview);\
        }

    #define to_bson_obj_list(key_, context_) \
        {\
            doc << key_ << open_array <<\
                [&](bsoncxx::builder::stream::array_context<> arr)\
            {\
                for (auto r : context_)\
                {\
                    std::string bstr = r->to_bson();\
                    auto bview = view(reinterpret_cast<const uint8_t*>(bstr.c_str()), bstr.length());\
                    arr << bsoncxx::builder::concatenate(bview);\
                }\
            }\
            << close_array;\
        }





    #define from_bson_value_string(key_, context_) if (bson[key_]) context_ = bson[key_].get_utf8().value.to_string();
    #define from_bson_value_double(key_, context_) if (bson[key_]) context_ = bson[key_].get_double();
    #define from_bson_value_int32(key_, context_) if (bson[key_]) context_ = bson[key_].get_int32();
    #define from_bson_value_int64(key_, context_) if (bson[key_]) context_ = bson[key_].get_int64();
    #define from_bson_value_datetime(key_, context_) if (bson[key_]) context_ = bson[key_].get_date().to_int64();
    #define from_bson_value_bool(key_, context_) if (bson[key_]) context_ = bson[key_].get_bool();
    #define from_bson_value_enum(key_, context_, type_) if (bson[key_]) {int v = bson[key_].get_int32();context_ = static_cast<type_>(v);}

    #define from_bson_vector_double(key_, context_) \
        context_.clear();\
        if (bson[key_] && bson[key_].type() == bsoncxx::type::k_array)\
        {\
            auto&& arr = bson[key_].get_array().value;\
            for (auto i : arr)\
            {\
                context_.push_back(i.get_double());\
            }\
        }

    #define from_bson_vector_float(key_, context_) \
        context_.clear();\
        if (bson[key_] && bson[key_].type() == bsoncxx::type::k_array)\
        {\
            auto&& arr = bson[key_].get_array().value;\
            for (auto i : arr)\
            {\
                context_.push_back(i.get_float());\
            }\
        }

    #define from_bson_vector_int32(key_, context_) \
        context_.clear();\
        if (bson[key_] && bson[key_].type() == bsoncxx::type::k_array)\
        {\
            auto&& arr = bson[key_].get_array().value;\
            for (auto i : arr)\
            {\
                context_.push_back(i.get_int32());\
            }\
        }

#define from_bson_vector_string(key_, context_) \
        context_.clear();\
        if (bson[key_] && bson[key_].type() == bsoncxx::type::k_array)\
        {\
            auto&& arr = bson[key_].get_array().value;\
            for (auto i : arr)\
            {\
                context_.push_back(i.get_utf8().value.to_string());\
            }\
        }

    #define from_bson_array_double(key_, context_, count) \
		if (bson[key_] && bson[key_].type()==bsoncxx::type::k_array)\
        {\
            auto&& arr = bson[key_].get_array().value;\
            for(int i = 0; i < count; ++i)\
                context_[i] = arr[i].get_double();\
        }

    #define from_bson_array_int32(key_, context_, count) \
		if (bson[key_] && bson[key_].type()==bsoncxx::type::k_array)\
        {\
            auto&& arr = bson[key_].get_array().value;\
            for(int i = 0; i < count; ++i)\
                context_[i] = arr[i].get_int32();\
        }

    #define from_bson_obj(key_, context_, type_) \
		if (bson[key_])\
        {\
            auto str = bson[key_].get_document().value;\
            context_ = type_::New();\
            context_->from_bson(std::string((char*)str.data(), str.length()));\
        }

    #define from_bson_obj_list(key_, context_, type_) \
		context_.clear();\
        if (bson[key_] && bson[key_].type() == bsoncxx::type::k_array)\
        {\
            auto&& arr = bson[key_].get_array().value;\
            for (auto i : arr)\
            {\
                auto str = i.get_document().value;\
                auto r = type_::New();\
                r->from_bson(std::string((char*)str.data(), str.length()));\
                context_.push_back(r);\
            }\
        }

}//end namespace dtl